package com.quwan.security.auth;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.quwan.app.service.MemberService;
import com.quwan.common.BaseService;
import com.quwan.common.Check;
import com.quwan.common.enums.*;
import com.quwan.common.exception.ExpCode;
import com.quwan.common.exception.ResponseException;
import com.quwan.component.AppleComponent;
import com.quwan.component.AsyncTaskComponent;
import com.quwan.component.TencentComponent;
import com.quwan.constans.AppConst;
import com.quwan.constans.CacheKey;
import com.quwan.entity.TGuest;
import com.quwan.entity.TMember;
import com.quwan.mapper.TGuestMapper;
import com.quwan.mapper.TMemberMapper;
import com.quwan.security.JwtTokenUtil;
import com.quwan.security.auth.vo.AppLoginParams;
import com.quwan.security.auth.vo.LoginResult;
import com.quwan.security.user.JwtUserDetails;
import com.quwan.utils.redis.Redis;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.function.Function;

/**
 * @author quan
 * @date 2021-12-08 21:49
 */
@RequiredArgsConstructor
@Component("appLogin")
public class AppLoginService extends BaseService {
    private static final String login_success = "1";
    private static final Long PUSH_MSG_EXPIRE = 60L;
    private final TMemberMapper memberMapper;
    private final AppleComponent appleComponent;
    private final TencentComponent tencentComponent;
    private final JwtTokenUtil jwtTokenUtil;
    private final TGuestMapper guestMapper;
    private final AsyncTaskComponent taskComponent;
    private final AuthenticationManager authenticationManager;
    private final MemberService memberService;
    private final Redis redis;
    @Value("${member.head}")
    private String defImgUrl;
    @Autowired
    private HttpServletRequest request;


    /**
     * 密码登录
     *
     * @return
     */
    protected Function<AppLoginParams, LoginResult> password() {
        return appLoginParams -> {
            String account = String.format("%s@@" + AppConst.MEMBER_MODULE, appLoginParams.getMobile());
            Authentication authentication = null;
            try {
                authentication =
                        authenticationManager.authenticate(
                                new UsernamePasswordAuthenticationToken(account, appLoginParams.getPassword()));
            } catch (ResponseException e) {
                throw e;
            } catch (BadCredentialsException e) {
                throw new ResponseException(ExpCode.LOGIN_ERR.getCode(), ExpCode.LOGIN_ERR.getMsg());
            } catch (AuthenticationException e) {
                throw new ResponseException(ExpCode.LOGIN_ERR.getCode(), ExpCode.LOGIN_ERR.getMsg());
            }
            String token = jwtTokenUtil.generateToken((UserDetails) authentication.getPrincipal());
            taskComponent.addLoginRecord(Long.parseLong(String.valueOf(jwtTokenUtil.getClaimsInfo(token).get("userId"))), LoginStatusEnum.LOGIN.getType(), request.getHeader(DeviceEnum.HEADER));
            return new LoginResult().setCode(login_success).setToken(token);
        };
    }

    /**
     * 手机  登录
     *
     * @return
     */
    protected Function<AppLoginParams, LoginResult> phone() {
        return appLoginParams -> {
            memberService.authPhoneCode(appLoginParams.getMobile(), appLoginParams.getPhoneCode());
            TGuest guest = _authGuest(appLoginParams.getGuestId());
            TMember memberInfo = memberMapper.selectOne(
                    new LambdaQueryWrapper<TMember>()
                            .select(TMember::getId, TMember::getAutoId, TMember::getPassword, TMember::getMobile, TMember::getStatus)
                            .eq(TMember::getMobile, appLoginParams.getMobile())

            );

            if (memberInfo == null) {
                //首次登录
                TMember record =
                        new TMember()
                                .setMobile(appLoginParams.getMobile())
                                .setNickName("user" + (redis.incr(CacheKey.USER.userCode)))
                                .setImgUrl(defImgUrl)
                                .setSource(request.getHeader(DeviceEnum.HEADER) == null ? LoginStatusEnum.REGISTRY.getType() : Integer.parseInt(request.getHeader(DeviceEnum.HEADER)))
                                .setId(guest == null ? IdWorker.getId() : guest.getId());
                if (StringUtils.isNotBlank(appLoginParams.getParentId()))
                    record.setParentId(Long.parseLong(appLoginParams.getParentId()));
                if (memberMapper.insert(record) != 1) {
                    logger.error("会员注册失败 mobile::{}", appLoginParams.getMobile());
                    throw new ResponseException(ExpCode.PHONE_REGISTER_FAIL.getCode(), ExpCode.PHONE_REGISTER_FAIL.getMsg());
                }
                taskComponent.addLoginRecord(record.getId(), LoginStatusEnum.REGISTRY.getType(), null);
               // redis.setListener(RedisListenerEnum.auto_push_msg.getKey() + RedisListenerEnum.split + record.getId(), 0, PUSH_MSG_EXPIRE);
                memberInfo = record;
                if (guest != null)
                    _updGuest(appLoginParams.getGuestId());
            } else if (memberInfo.getStatus().equals(MemberStatusEnum.DISABLED.getCode()))
                throw new ResponseException(ExpCode.ACCOUNT_DISABLED.getCode(), ExpCode.ACCOUNT_DISABLED.getMsg());


            String token = jwtTokenUtil.generateToken(JwtUserDetails.I(memberInfo.getId() + "", memberInfo.getMobile() + "", memberInfo.getPassword(), AppConst.MEMBER_MODULE));
            taskComponent.addLoginRecord(memberInfo.getId(), LoginStatusEnum.LOGIN.getType(), request.getHeader(DeviceEnum.HEADER));
            return new LoginResult().setCode(login_success).setToken(token);
        };
    }

    /**
     * wx  登录
     *
     * @return
     */
    protected Function<AppLoginParams, LoginResult> wx() {
        return appLoginParams -> {
            JSONObject authInfo = tencentComponent.oauth2(appLoginParams.getCode());
            String openId = authInfo.getString("openid");
            Check.state(StringUtils.isNotBlank(openId), ExpCode.AUTH_CODE_FAIL);
            appLoginParams.setOpenId(openId);
            return new LoginResult()
                    .setCode(login_success)
                    .setToken(_oauth2Login(appLoginParams, TMember::getWxOpenId, new TMember().setWxOpenId(openId).setNickName(authInfo.getString("nickname"))));

        };
    }

    /**
     * ios 登录处理
     *
     * @return
     */
    protected Function<AppLoginParams, LoginResult> ios() {
        return appLoginParams -> {
            String openId = null;
            //解析code
            try {
                openId = appleComponent.authToken(appLoginParams.getCode());
            } catch (Exception e) {
                logger.error("ios exp::{}", e.getMessage());
                return new LoginResult().setCode(ExpCode.AUTH_CODE_FAIL.getCode() + "");
            }
            Check.state(StringUtils.isNotBlank(openId), ExpCode.AUTH_CODE_FAIL);
            appLoginParams.setOpenId(openId);
            return new LoginResult()
                    .setCode(login_success)
                    .setToken(_oauth2Login(appLoginParams, TMember::getIosToken, new TMember().setIosToken(openId)));

        };
    }

    /**
     * 游客登录
     *
     * @return
     */
    protected Function<AppLoginParams, LoginResult> guest() {
        return appLoginParams -> {
            String guestId = null;
            TGuest guest = guestMapper.selectOne(
                    new QueryWrapper<>(
                            new TGuest().setDeviceId(appLoginParams.getGuestId())));

            if (guest == null) {
                guest = new TGuest()
                        .setDeviceId(appLoginParams.getGuestId())
                        .setName("guest_" + (guestMapper.selectCount(new QueryWrapper<>()) + 1))
                        .setConvertMember(CommonStatus.DISABLED.getType());

                if (guestMapper.insert(guest) != 1)
                    throw new ResponseException(ExpCode.LOGIN_GUEST_FAIL.getCode(), ExpCode.LOGIN_GUEST_FAIL.getMsg());

                guestId = guest.getId().toString();

            }
            return new LoginResult().setCode(login_success).setGuestId(guestId);
        };
    }


    private String _oauth2Login(AppLoginParams params, SFunction<TMember, ?> column, TMember record) {
        //openid 查询一下用户
        TMember member = memberMapper.selectOne(
                new LambdaQueryWrapper<TMember>()
                        .select(TMember::getId, TMember::getAutoId, TMember::getPassword, TMember::getMobile, TMember::getStatus)
                        .eq(column, params.getOpenId())
        );
        //检查下账号是否被禁用
        if (member != null && member.getStatus().equals(MemberStatusEnum.DISABLED.getCode()))
            throw new ResponseException(ExpCode.ACCOUNT_DISABLED.getCode(), ExpCode.ACCOUNT_DISABLED.getMsg());

        if (member != null) {
            taskComponent.addLoginRecord(member.getId(), LoginStatusEnum.LOGIN.getType(), request.getHeader(DeviceEnum.HEADER) == null ? DeviceEnum.ANDROID.getType() + "" : request.getHeader(DeviceEnum.HEADER));
            //用户存在
            return jwtTokenUtil.generateToken(JwtUserDetails.I(member.getId() + "", member.getMobile(), member.getPassword(), AppConst.MEMBER_MODULE));
        } else {
            //没有该用户 就用openId注册该用户
            //判断一下有没进行过游客登录 如果有 取游客id作为用户id
            TGuest guest = _authGuest(params.getGuestId());

            record.setId(guest == null ? IdWorker.getId() : guest.getId())
                    .setImgUrl(defImgUrl)
                    .setSource(request.getHeader(DeviceEnum.HEADER) == null ? DeviceEnum.ANDROID.getType() : Integer.parseInt(request.getHeader(DeviceEnum.HEADER)));
            if (StringUtils.isBlank(record.getNickName()))
                record.setNickName("user" + redis.incr(CacheKey.USER.userCode));
            //有推荐用户 设置推广用户的id
            if (StringUtils.isNotBlank(params.getParentId()))
                record.setParentId(Long.parseLong(params.getParentId()));

            if (memberMapper.insert(record) == 1) {
                //注册成功 生成token参数返回

                if (guest != null)
                    //是游客 更新一下游客表字段 代表游客已经转化成会员
                    _updGuest(guest.getId().toString());
                //记录一下用户注册操作
                taskComponent.addLoginRecord(record.getId(), LoginStatusEnum.REGISTRY.getType(), null);
               // redis.setListener(RedisListenerEnum.auto_push_msg.getKey() + RedisListenerEnum.split + record.getId(), 0, PUSH_MSG_EXPIRE);
                return jwtTokenUtil.generateToken(JwtUserDetails.I(record.getId() + "", "undefined", "undefined", AppConst.MEMBER_MODULE));
            }
            throw new ResponseException(ExpCode.LOGIN_FAIL.getCode(), ExpCode.LOGIN_FAIL.getMsg());
        }
    }

    /**
     * 转换一下游客状态  游客 ->会员
     *
     * @param guestId
     */
    private void _updGuest(String guestId) {
        guestMapper
                .updateById(
                        new TGuest()
                                .setId(Long.parseLong(guestId.trim()))
                                .setConvertMember(CommonStatus.NORMAL.getType()));
    }

    /**
     * 校验是否游客
     *
     * @param guestId
     * @return
     */
    private TGuest _authGuest(String guestId) {
        if (StringUtils.isBlank(guestId))
            return null;
        return guestMapper.selectById(guestId);

    }

}